package nl.windgazer.arachne.services.externalsource;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Locale;

import nl.windgazer.arachne.services.ArachnePrivileges;
import nl.windgazer.arachne.services.ContentManagerService;

import org.apache.log4j.Logger;
import org.apache.tapestry5.internal.services.ContextResource;
import org.apache.tapestry5.services.Context;
import org.apache.tapestry5.services.Request;

public class HtmlContentManager implements ContentManagerService {

	private static final Logger logger = Logger.getLogger(HtmlContentManager.class);
	private static final String ext = ".html";
	private static final String BLOCK_HTML_START = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><title>Live Edit</title>\n";
	private static final String BlOCK_SCRIPT_START = "<script type=\"text/javascript\">//<!-- 2B|!2B\n";
	private static final String BlOCK_SCRIPT_END = "\n//==42 --></script>\n";
	private static final String INC_SCRIPT_START = "<script type=\"text/javascript\" src=\"";
	private static final String INC_SCRIPT_END = "\"></script>\n";
	private static final String BLOCK_STYLE_START = "\n<style type=\"text/css\">\n";
	private static final String BLOCK_STYLE_END = "\n</style>\n";
	private static final String INC_STYLE_START = "<link rel=\"stylesheet\" type=\"text/css\" media=\"screen,print\" href=\"";
	private static final String INC_STYLE_END = "\" />\n";
	private static final String BLOCK_BODY_START = "</head>\n<body>\n";
	private static final String BLOCK_BODY_END = "\n</body>\n</html>";

	private Locale locale;
    private Context context;
    private Request request;

    private ArachnePrivileges privileges;
	private boolean transEnabled;
	private String transRole;

	private boolean relative;
    private String path;

	public HtmlContentManager(
				Locale locale
				, Context context
				, Request request
				, ArachnePrivileges ap
				, boolean transEnabled
				, String transRole
				, final boolean relative
				, final String path
	) {
		this.locale = locale;
		this.context = context;
		this.request = request;
		this.privileges = ap;
		this.transEnabled = transEnabled;
		this.transRole = transRole;

		this.path = path;
		this.relative = relative;
	}

	public ManagedContent getContent(String path, String key) {
		//Try to obtain an html-file based on the path, key and locale
		final StringBuilder basename = new StringBuilder(32).append(path);
		
		if (basename.lastIndexOf("/") < (basename.length() - 1)) basename.append('-'); //Add divider for non-index pages.
		basename.append(key); //'key' will be the filename...

		return getContent(basename.toString());
	}

	public String getBaseUrl(String filePath) {
		final File f = context.getRealFile(getFilePath(filePath));
		final String parent = f.getParent() + "/";
		final String contextRoot = context.getRealFile("/").getAbsolutePath();
		return parent.replace(contextRoot, "");
	}

	public String getFilePath(String path) {
		if (logger.isDebugEnabled()) {
			logger.debug("'" + path + "'");
		}
		final ContextResource cr = new ContextResource(context, path);
		StringBuilder contextPath = new StringBuilder(cr.getPath());
	    if (contextPath.indexOf(request.getContextPath()) == 0) contextPath.delete(0, request.getContextPath().length()); //Remove context-path, if there is one (could be running as root)
	    if (contextPath.indexOf(locale.toString()) == 1) contextPath.delete(0, locale.toString().length() + 1); //Remove ThreadLocale from path
	    contextPath.insert(0, this.path); //Pre-pend the '/cms' directory.
	    
	    return contextPath.toString();
	}

	public File getFile(String path) {
	    return relative?context.getRealFile(getFilePath(path)):new File(getFilePath(path));
	}

	public boolean canStoreContent() {
		return transEnabled;
	}

	public boolean storeContent(ManagedContent content) {
		if (transEnabled && privileges.hasRole(transRole)) {
			final File targetFile = getFile(new StringBuilder(content.getUID()).append('_').append(locale.toString()).append(ext).toString());
	
			//Create HTML-page
			final StringBuffer finalContent = new StringBuffer(BLOCK_HTML_START);
			if (content.getScriptBlock() != null && content.getScriptBlock().length() > 0) {
				finalContent.append(BlOCK_SCRIPT_START);
				finalContent.append(content.getScriptBlock());
				finalContent.append(BlOCK_SCRIPT_END);
			}
			if (content.getStyleBlock() != null && content.getStyleBlock().length() > 0) {
				finalContent.append(BLOCK_STYLE_START);
				finalContent.append(content.getStyleBlock());
				finalContent.append(BLOCK_STYLE_END);
			}
			if (content.getScripts()!=null) {
				for (String script : content.getScripts()) {
					finalContent.append(INC_SCRIPT_START);
					finalContent.append(script);
					finalContent.append(INC_SCRIPT_END);
				}
			}
			if (content.getStyles()!=null) {
				for (String script : content.getStyles()) {
					finalContent.append(INC_STYLE_START);
					finalContent.append(script);
					finalContent.append(INC_STYLE_END);
				}
			}
			finalContent.append(BLOCK_BODY_START);
			finalContent.append(content.getContent());
			finalContent.append(BLOCK_BODY_END);
	
			logger.info(finalContent);
	
			//Backup existing content, just in case...
			if (targetFile.exists()) {
				final StringBuffer sb = new StringBuffer(targetFile.getAbsolutePath()).append('.').append(System.currentTimeMillis()).append(".bak");
				final File backup = new File(sb.toString());
				if (!targetFile.renameTo(backup)) {
					return false; //Rename fails, cannot overwrite...
				}
			}
	
			//Create new file...
			try {
				targetFile.getParentFile().mkdirs();
				if (!targetFile.createNewFile()) return false; //Creation fails, cannot store...
			} catch (IOException e) {
				logger.error(e);
				return false; //Something unexpected breaks, cannot store...
			}
	
			try {
				final FileWriter outputWriter = new FileWriter(targetFile);
				outputWriter.write(finalContent.toString());
				outputWriter.close();
			} catch (IOException e) {
				logger.error(e);
				return false; //Something unexpected breaks, cannot store...
			} 
			return true;//Reaching here should result in a successful write...
		}
		return false;
	}

	public ManagedContent getContent(String uid) {
		final StringBuilder basename = new StringBuilder(uid);

		File origin = getFile(new StringBuilder(basename).append('_').append(locale.toString()).append(ext).toString());
		if (!origin.exists()) {
			origin = getFile(new StringBuilder(basename).append('_').append(locale.getLanguage()).append(ext).toString());
			if (!origin.exists()) {
				origin = getFile(new StringBuilder(basename).append('_').append(locale.getCountry()).append(ext).toString());
				if (!origin.exists())
					origin = getFile(new StringBuilder(basename).append(ext).toString());
			}
		}
		//If I still couldn't get a file-lock, it's over...
		if (!origin.exists()) {
			logger.error(origin.getAbsolutePath() + " does not exist!");
		}
		try {
			return new HtmlManagedContent(origin, uid, getBaseUrl(uid));
		} catch (FileNotFoundException e) {
			//Since checks are made for existence, this can't possibly occur...
			logger.error("Failed to find a file that is supposed to .exists()...");
			return null;
		}
	}
}
